package cn.wyj.basic.service.impl;

import cn.wyj.basic.config.BussinessException;
import cn.wyj.basic.constant.BaseContant;
import cn.wyj.basic.deo.WechatLoginDto;
import cn.wyj.basic.jwt.JwtUtils;
import cn.wyj.basic.jwt.RsaUtils;
import cn.wyj.basic.jwt.UserInfo;
import cn.wyj.basic.service.ILoginService;
import cn.wyj.basic.util.AjaxResult;
import cn.wyj.basic.util.HttpUtil;
import cn.wyj.basic.util.MD5Utils;
import cn.wyj.basic.util.StrUtils;
import cn.wyj.basic.vo.LoginVo;
import cn.wyj.system.domain.Menu;
import cn.wyj.system.domain.Permission;
import cn.wyj.system.mapper.MenuMapper;
import cn.wyj.system.mapper.PermissionMapper;
import cn.wyj.user.domain.Logininfo;
import cn.wyj.user.domain.User;
import cn.wyj.user.domain.Wxuser;
import cn.wyj.user.mapper.LogininfoMapper;
import cn.wyj.user.mapper.UserMapper;
import cn.wyj.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import javassist.runtime.DotClass;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hibernate.validator.internal.util.privilegedactions.GetClassLoader;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class ILoginServiceImpl implements ILoginService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Value("${jwt.rsa.pri}")
    private String privateRsaKey;

    /**
     * 登录
     * @param vo
     * @return
     */
    @Override
    public Map<String,Object> account(LoginVo vo) {
        // 参数为空校验
        // 判断账户是否已经存在logininfo
        Logininfo logininfo =  logininfoMapper.findByUsername(vo.getUsername(),vo.getType());
        if (null == logininfo){
            // 其实这个是账户错误，因为查不到相应的用户名
            throw new BussinessException("账户或者密码错误!");
        }

        // 判断密码是否正确
        String encrypPassword = MD5Utils.encrypByMd5(logininfo.getSalt() + vo.getPassword());
        if (StringUtils.isBlank(encrypPassword) || !encrypPassword.equals(logininfo.getPassword())){
            // 这个就是密码错误了
            throw new BussinessException("账号或者密码错误");
        }
        // 将token和logininfo添加到redis中
        Map<String, Object> map = getMap(logininfo);

        return map;
    }

    // 抽取 将token和logininfo添加到redis中
    private Map<String, Object> getMap(Logininfo logininfo) {
        // 使用jwt方案   解析token里面内容 获取密钥
        PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(privateRsaKey).getFile());
        //
        Map<String, Object> map = new HashMap<>();
        UserInfo user = new UserInfo();
        user.setLogininfo(logininfo);
        // 如果是后端，我们将需要将权限放到token里面
        if (0 == logininfo.getType()){
            // 获取菜单权限 sn
            List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
            // 按钮权限
            List<Menu> menus = menuMapper.findByLogininfoId(logininfo.getId());
            // 存到userInfo对象里
            user.setPermissions(permissions);
            user.setMenus(menus);
            // 存到集合里
            map.put("permissions", permissions);
            map.put("menus", menus);
        }
        // 复制token
        String token = JwtUtils.generateTokenExpireInMinutes(user,privateKey, 30);

        // 将敏感字段置空
        logininfo.setPassword("");
        logininfo.setPhone("");
        logininfo.setSalt("");
        // 返回给前端

        map.put("token",token);
        map.put("logininfo", logininfo);
        return map;
    }

    /**
     * 微信登录
     */
    @Override
    public AjaxResult wechatAccount(WechatLoginDto dto) {
        // 验证参数
        if (StringUtils.isBlank(dto.getCode())){
            throw new BussinessException("参数不能为空");
        }
        // 根据code调用微信接口获取openid和token
        Pair<String, String> pair = getOpenidAndToken(dto.getCode());

        // 根据openid 去wx_user查询是否有数据
        Wxuser wxuser =  wxuserMapper.findByOpenid(pair.getRight());
        if (wxuser != null && null != wxuser.getUserId()){
            // 这个人之前绑定过了 直接去登录
            // 返给前端token logininfo
            Logininfo logininfo = logininfoMapper.findByUserId(wxuser.getUserId());

            Map<String, Object> map = getMap(logininfo);

            return AjaxResult.me().success(map);
        }
        // 当前人没有绑定
        StringBuffer sb = new StringBuffer()
                .append("?accessToken=")
                .append(pair.getLeft())
                .append("&openId=").append(pair.getRight());
        return AjaxResult.me().fail("nobinder").setResultObj(sb);
    }

    /**
     * 微信登录绑定
     * @param dto
     */
    @Override
    public Map<String,Object> wechatBinder(WechatLoginDto dto) {
        // 校验参数非空
        // 校验验证码是否正确
        verifySmsCode(dto);
        // 调用微信接口，获取用户信息
        String url = String.format(BaseContant.WxLoginCons.WX_LOGIN_USER_INFO,dto.getAccessToken(), dto.getOpenId());
        String str = HttpUtil.httpGet(url);
        // json字符串转换为实体
        Wxuser wxuser = JSONObject.parseObject(str, Wxuser.class);
//        System.out.println(wxuser);
        // 存
        // 查一下user有没有，没有的话，存3张表， 有的话存一张表
        User user = userMapper.findByPhone(dto.getPhone());
        Logininfo logininfo = new Logininfo();
        if (null == user){
            // 初始化user对象
            user = initUser(dto);
            // 初始化logininfo对象
            logininfo = initLogininfo(user);
            // 保存logininfo
            logininfoMapper.add(logininfo);
            // 保存User
            user.setLogininfoId(logininfo.getId());
            userMapper.add(user);
        }else {
            // 获取logininfo对象
            logininfo = logininfoMapper.findByUserId(user.getId());
        }
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);

        // 保存wxuser
        return getMap(logininfo);
    }
    private Logininfo initLogininfo(User user){
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        return logininfo;
    }


    private User initUser(WechatLoginDto dto){
        // 盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 密码
        String password = salt + "1";
        String encryPwd = MD5Utils.encrypByMd5(password);
        return User
                .builder()
                .username(dto.getPhone())
                .phone(dto.getPhone())
                .salt(salt)
                .password(encryPwd)
                .state(1)
                .createtime(new Date())
                .build();
    }

    /**
     * 抽取的验证码验证
     * @param dto
     */
    private void verifySmsCode(WechatLoginDto dto) {
        String  redisSmsCode = (String) redisTemplate.opsForValue().get(String.format(BaseContant.VerfityCodeCons.BINDER_SMSCODE_PHONE,
                dto.getPhone()));
        if (StringUtils.isBlank(redisSmsCode)){
            throw new BussinessException("验证码错误!");
        }
        //
        String smsCode =  redisSmsCode.split(":")[0];
        if (!smsCode.equals(dto.getVerifyCode())){
            throw new BussinessException("验证码错误!");
        }
    }

    // 将通过code获取 openid和token的方法，抽取出来
    private Pair<String,String> getOpenidAndToken(String code){
        // 根据code调用微信接口获取openid和token
        String tokenUrl = String.format(BaseContant.WxLoginCons.WX_LOGIN_TOKEN_URL,
                BaseContant.WxLoginCons.WX_LOGIN_APPID,BaseContant.WxLoginCons.WX_LOGIN_SECRET,
                code);
        String str = HttpUtil.httpGet(tokenUrl);
        // 将json字符串转成json对象
        JSONObject jsonObject = JSONObject.parseObject(str);
        // 获取到其中的属性
        String accessToken = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        return Pair.of(accessToken,openid);
    }


    /**
     * 密码生成测试
     * @param args
     */
    public static void main(String[] args) {
        RedisTemplate<Object, Object> re = new RedisTemplate<>();
        // 盐值
        String salt = StrUtils.getComplexRandomString(32); // VLn9KHntVCPnxC8rO1CFTLsuDwknQhOi
        // 密码
        String password = salt + 1;
        System.out.println(password);
        String encryPwd = MD5Utils.encrypByMd5(password); // a4cf804390a81a88416b60341963effc
        System.out.println(encryPwd);

    }
}
